// Ninf-G-XX, ver. 0.1
// Copyright 2011 National Institute of Advanced Industrial Science and Technology
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include "Global.hpp"
#include "DataInputStream.hpp"
#include "FpInputStream.hpp"
#include "FunctionInterfaceImpl.hpp"
#include "Exception.hpp"

using namespace ninf::client;

Global Global :: global;

Global :: Global ()
  : logger(), interfaceMap(),pid((int)getpid()), sessionManager(pid),
    condorBinDir(NULL), runDir(NULL), executingJob(0), maxExecuting(30),
    universe(UNIVERSE_VANILLA)
{
}

// read an interfaces in the directory and store them in the interfaceMap
void
Global :: readInterfaceDir(const char * dirname){
  DIR * dir;
  char buffer[1000];
  struct dirent * tdirent;

  if (dirname == NULL)
    EXCEPTION("info_dir is NULL");

  if ((dir = opendir(dirname)) == NULL){
    fprintf(stderr, "can't open dir: %s\n", dirname);
    return;
  }
  const char * infoHead = "_stub_";
  const char * infoTail = ".info";

  while ((tdirent = readdir(dir)) != NULL){
    if (strncmp(tdirent->d_name, infoHead, strlen(infoHead)) == 0 &&
	strncmp(tdirent->d_name + strlen(tdirent->d_name) - strlen(infoTail),
		infoTail, strlen(infoTail)) == 0) {
      strncpy(buffer, dirname, 1000 - strlen(tdirent->d_name) - 2);
      strcat(buffer, "/");
      strcat(buffer, tdirent->d_name);
      readInterface(buffer);
    }
  }
  closedir(dir);
}

// read an interface and store it in the interfaceMap
void
Global :: readInterface(const char * filename){
  if (condorBinDir != NULL)
    free(condorBinDir);
  FpInputStream is(filename);
  DataInputStream dis(&is);
  readInterface(dis);
  is.close();
}

// read an interface and store it in the interfaceMap
void
Global :: readInterface(DataInputStream & dis){
  // load and create interface object
  FunctionInterfaceImpl * interface =
    new FunctionInterfaceImpl(dis);

  // generate interface name 'entry/module'
  const char * moduleName = interface->moduleName;
  const char * entryName  = interface->entryName;
  char * buffer = new char[strlen(moduleName) + strlen(entryName) + 1];
  sprintf(buffer, "%s/%s", moduleName, entryName);

  logger.log("\tinterface %s registered\n", buffer);

  // put the interface to the map
  interfaceMap[buffer] = interface;
}


bool
Global :: readConf(const char *filename){
  char buffer[MAX_LINE_LENGTH];

  FILE * fp = fopen(filename, "r");
  if (fp == NULL){
    logger.log("%s",
	       (string("failed to open config file ") + filename).c_str());
    return false;
  }

  while (fgets(buffer, MAX_LINE_LENGTH -2, fp) != NULL){
    if (buffer[0] == '#')   // skip comment line
      continue;

    // seperate
    char * command = strtok(buffer, " \t\n=");
    if (command == NULL)
      continue;

    if (strncasecmp(command, "info", 5) == 0){
      char * val = strtok(NULL, " \t\n=");
      try {
	readInterface(val);
      } catch (Exception e){
	logger.log("%s", e.message.c_str());
	return false;
      }
      continue;
    } else if (strncasecmp(command, "info_dir", 9) == 0){
      char * val = strtok(NULL, " \t\n=");
      readInterfaceDir(val);
    } else if (strncasecmp(command, "bin_dir", 8) == 0){
      while (true){
	char * val = strtok(NULL, " \t\n=");
	binDirs.push_back(strdup(val));
      }
    } else if (strncasecmp(command, "condor_bin_dir", 15) == 0){
      condorBinDir = strdup(strtok(NULL, " \t\n="));
    } else if (strncasecmp(command, "max_exec", 9) == 0){
      char * val = strtok(NULL, " \t\n=");
      maxExecuting = atoi(val);
    } else if (strncasecmp(command, "universe", 9) == 0){
      char * val = strtok(NULL, " \t\n=");
      if (strncasecmp(val, "vanilla",  8) == 0){
	universe = UNIVERSE_VANILLA;
      } else
      if (strncasecmp(val, "standard", 9) == 0){
	universe = UNIVERSE_STANDARD;
      } else
      if (strncasecmp(val, "globus",   7) == 0){
	universe = UNIVERSE_GLOBUS;
      } else {
	logger.log("Unknown universe specified in config: %s", val);
	return false;
      }
    }
  }
  return true;
}



int
Global :: parseArg(int argc, char ** argv){
  int i = 0;
  char ** index = argv;
  for (; *argv != NULL; argv++){

    if (strcasecmp(*argv, "-conf") == 0){
      char * confFilename = *(++argv);
      if (!readConf(confFilename))
	return -1;
    } else if (strcasecmp(*argv, "-run_dir") == 0){
      runDir = strdup(*(++argv));
    } else if (strcasecmp(*argv, "-vanilla") == 0){
      universe = UNIVERSE_VANILLA;
    } else if (strcasecmp(*argv, "-standard") == 0){
      universe = UNIVERSE_STANDARD;
    } else if (strcasecmp(*argv, "-globus") == 0){
      universe = UNIVERSE_GLOBUS;
    } else {
      i++;
      *(index++) = *(argv);
    }
  }
  *(index++) = *(argv);

  init();
  return i;
}

void
Global :: init(){
  if (binDirs.size() == 0){
    char buffer[1000];
    getcwd(buffer, 1000);
    char * cwd = strdup(buffer);
    binDirs.push_back(cwd);
  }

  char buffer0[1000];
  getcwd(buffer0, 1000);
  currentDir = strdup(buffer0);

  if (runDir == NULL)
    runDir = createRunDir();
}


/** create pid name sub dirctory and returns it */
char *
Global :: createRunDir(){
  char buffer[1000];
  sprintf(buffer, "%s/%d", currentDir, pid);
  mkdir(buffer, 0755);
  return strdup(buffer);
}
